package com.gateway.common.config;

import com.gateway.common.enums.TrieMatchModeEvent;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @program: api-gateway
 * @description:
 * @author: YuKai Fan
 * @create: 2025/2/5 21:49
 **/
public class GatewayConfig {

    private FileConfig file = new FileConfig();

    private ExcludePath exclude = new ExcludePath();

    private Health health = new Health();

    private FallbackPath fallback = new FallbackPath();

    private MatchCache matchCache = new MatchCache();

    private Scheduler scheduler = new Scheduler();

    private CrossFilterConfig cross = new CrossFilterConfig();

    private GatewayTrieConfig trie = new GatewayTrieConfig();

    public GatewayTrieConfig getTrie() {
        return trie;
    }

    public void setTrie(GatewayTrieConfig trie) {
        this.trie = trie;
    }

    public Scheduler getScheduler() {
        return scheduler;
    }

    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    public MatchCache getMatchCache() {
        return matchCache;
    }

    public void setMatchCache(MatchCache matchCache) {
        this.matchCache = matchCache;
    }

    public CrossFilterConfig getCross() {
        return cross;
    }

    public void setCross(CrossFilterConfig cross) {
        this.cross = cross;
    }

    public FileConfig getFile() {
        return file;
    }

    public void setFile(FileConfig file) {
        this.file = file;
    }

    public ExcludePath getExclude() {
        return exclude;
    }

    public void setExclude(ExcludePath exclude) {
        this.exclude = exclude;
    }

    public Health getHealth() {
        return health;
    }

    public void setHealth(Health health) {
        this.health = health;
    }

    public FallbackPath getFallback() {
        return fallback;
    }

    public void setFallback(FallbackPath fallback) {
        this.fallback = fallback;
    }

    /**
     * The type Exclude path.
     */
    public static class ExcludePath {

        private boolean enabled;

        private List<String> paths = new ArrayList<>();

        /**
         * Gets enabled.
         *
         * @return the enabled
         */
        public boolean getEnabled() {
            return enabled;
        }

        /**
         * Sets enabled.
         *
         * @param enabled the enabled
         */
        public void setEnabled(final boolean enabled) {
            this.enabled = enabled;
        }

        /**
         * Sets paths.
         *
         * @param paths the paths
         */
        public void setPaths(final List<String> paths) {
            this.paths = paths;
        }

        /**
         * get paths.
         *
         * @return paths paths
         */
        public List<String> getPaths() {
            return paths;
        }
    }

    /**
     * The type fallback path.
     */
    public static class FallbackPath {

        private boolean enabled;

        private List<String> paths = new ArrayList<>();

        /**
         * Gets enabled.
         *
         * @return the enabled
         */
        public boolean getEnabled() {
            return enabled;
        }

        /**
         * Sets enabled.
         *
         * @param enabled the enabled
         */
        public void setEnabled(final boolean enabled) {
            this.enabled = enabled;
        }

        /**
         * Sets paths.
         *
         * @param paths the paths
         */
        public void setPaths(final List<String> paths) {
            this.paths = paths;
        }

        /**
         * get paths.
         *
         * @return paths paths
         */
        public List<String> getPaths() {
            return paths;
        }
    }

    /**
     * The type Health.
     */
    public static class Health {

        private boolean enabled;

        private List<String> paths = new ArrayList<>();

        /**
         * Gets enabled.
         *
         * @return the enabled
         */
        public boolean getEnabled() {
            return enabled;
        }

        /**
         * Sets enabled.
         *
         * @param enabled the enabled
         */
        public void setEnabled(final boolean enabled) {
            this.enabled = enabled;
        }

        /**
         * Sets paths.
         *
         * @param paths the paths
         */
        public void setPaths(final List<String> paths) {
            this.paths = paths;
        }

        /**
         * get paths.
         *
         * @return paths paths
         */
        public List<String> getPaths() {
            return paths;
        }
    }

    /**
     * The type File config.
     */
    public static class FileConfig {

        private boolean enabled;

        private Integer maxSize = 10;

        /**
         * Gets enabled.
         *
         * @return the enabled
         */
        public boolean getEnabled() {
            return enabled;
        }

        /**
         * Sets enabled.
         *
         * @param enabled the enabled
         */
        public void setEnabled(final boolean enabled) {
            this.enabled = enabled;
        }

        /**
         * Gets file max size.
         *
         * @return the file max size
         */
        public Integer getMaxSize() {
            return maxSize;
        }

        /**
         * Sets file max size.
         *
         * @param maxSize the file max size
         */
        public void setMaxSize(final Integer maxSize) {
            this.maxSize = maxSize;
        }
    }

    /**
     * the match cache.
     */
    public static class MatchCache {

        private boolean selectorEnabled;

        /**
         * Max free memory, unit mb.
         */
        private Integer maxSelectorFreeMemory = 256;

        /**
         * Get selector cache enabled.
         *
         * @return the enabled
         */
        public boolean getSelectorEnabled() {
            return selectorEnabled;
        }

        /**
         * Set selector enabled.
         *
         * @param selectorEnabled the enabled
         */
        public void setSelectorEnabled(final boolean selectorEnabled) {
            this.selectorEnabled = selectorEnabled;
        }


        /**
         * Gets maxFreeMemory.
         *
         * @return the maxFreeMemory
         */
        public Integer getMaxSelectorFreeMemory() {
            return maxSelectorFreeMemory;
        }

        /**
         * Sets maxFreeMemory.
         *
         * @param maxSelectorFreeMemory the maxFreeMemory
         */
        public void setMaxSelectorFreeMemory(final Integer maxSelectorFreeMemory) {
            this.maxSelectorFreeMemory = maxSelectorFreeMemory;
        }
    }

    /**
     * The Cross Filter Config.
     */
    public static class CrossFilterConfig {

        private static final Set<String> DEFAULT_ALLOWED_HEADERS;

        static {
            DEFAULT_ALLOWED_HEADERS = new HashSet<String>() {
                {
                    add("x-requested-with");
                    add("authorization");
                    add("Content-Type");
                    add("Authorization");
                    add("credential");
                    add("X-XSRF-TOKEN");
                    add("token");
                    add("username");
                    add("client");
                }
            };
        }

        private boolean enabled;

        /**
         * Comma-separated of “header”.
         */
        private String allowedHeaders = "";

        /**
         * Comma-separated of “method”.
         */
        private String allowedMethods = "*";

        private AllowedOriginConfig allowedOrigin = new AllowedOriginConfig();

        private boolean allowedAnyOrigin;

        private String allowedExpose = "";

        private String maxAge = "18000";

        private boolean allowCredentials;

        /**
         * wrapper the headers.
         *
         * @param headers headers
         * @return wrapped headers
         */
        private String wrapperHeaders(final String headers) {
            final Set<String> headerSet = DEFAULT_ALLOWED_HEADERS;
            if (StringUtils.isNotEmpty(headers)) {
                headerSet.addAll(Stream.of(headers.split(",")).collect(Collectors.toSet()));
            }
            return String.join(",", headerSet);
        }

        /**
         * Gets enabled.
         *
         * @return the enabled
         */
        public boolean getEnabled() {
            return enabled;
        }

        /**
         * Sets enabled.
         *
         * @param enabled the enabled
         */
        public void setEnabled(final boolean enabled) {
            this.enabled = enabled;
        }

        /**
         * Gets the value of allowedHeaders.
         *
         * @return the value of allowedHeaders
         */
        public String getAllowedHeaders() {
            return allowedHeaders = wrapperHeaders(allowedHeaders);
        }

        /**
         * Sets the allowedHeaders.
         *
         * @param allowedHeaders allowedHeaders
         */
        public void setAllowedHeaders(final String allowedHeaders) {
            this.allowedHeaders = wrapperHeaders(allowedHeaders);
        }

        /**
         * Gets the value of allowedMethods.
         *
         * @return the value of allowedMethods
         */
        public String getAllowedMethods() {
            return allowedMethods;
        }

        /**
         * Sets the allowedMethods.
         *
         * @param allowedMethods allowedMethods
         */
        public void setAllowedMethods(final String allowedMethods) {
            this.allowedMethods = allowedMethods;
        }

        /**
         * Gets the value of allowedOrigin.
         *
         * @return the value of allowedOrigin
         */
        public AllowedOriginConfig getAllowedOrigin() {
            return allowedOrigin;
        }

        /**
         * Sets the allowedOrigin.
         *
         * @param allowedOrigin allowedOrigin
         */
        public void setAllowedOrigin(final AllowedOriginConfig allowedOrigin) {
            this.allowedOrigin = allowedOrigin;
        }

        /**
         * Gets the value of allowedAnyOrigin.
         *
         * @return the value of allowedAnyOrigin
         */
        public boolean isAllowedAnyOrigin() {
            return allowedAnyOrigin;
        }

        /**
         * Sets the allowedExpose.
         *
         * @param allowedAnyOrigin allowedExpose
         */
        public void setAllowedAnyOrigin(final boolean allowedAnyOrigin) {
            this.allowedAnyOrigin = allowedAnyOrigin;
        }

        /**
         * Gets the value of allowedExpose.
         *
         * @return the value of allowedExpose
         */
        public String getAllowedExpose() {
            return allowedExpose;
        }

        /**
         * Sets the allowedExpose.
         *
         * @param allowedExpose allowedExpose
         */
        public void setAllowedExpose(final String allowedExpose) {
            this.allowedExpose = allowedExpose;
        }

        /**
         * Gets the value of maxAge.
         *
         * @return the value of maxAge
         */
        public String getMaxAge() {
            return maxAge;
        }

        /**
         * Sets the maxAge.
         *
         * @param maxAge maxAge
         */
        public void setMaxAge(final String maxAge) {
            this.maxAge = maxAge;
        }

        /**
         * Gets the value of allowCredentials.
         *
         * @return the value of allowCredentials
         */
        public boolean isAllowCredentials() {
            return allowCredentials;
        }

        /**
         * Sets the allowCredentials.
         *
         * @param allowCredentials allowCredentials
         */
        public void setAllowCredentials(final boolean allowCredentials) {
            this.allowCredentials = allowCredentials;
        }

        /**
         * the cors allowedOrigin config.
         */
        public static class AllowedOriginConfig {

            private String spacer = ".";

            private String domain;

            private Set<String> prefixes = new HashSet<>();

            private Set<String> origins;

            private String originRegex;

            /**
             * Gets the spacer.
             *
             * @return the value of spacer
             */
            public String getSpacer() {
                return spacer;
            }

            /**
             * Sets the spacer.
             *
             * @param spacer spacer
             */
            public void setSpacer(final String spacer) {
                this.spacer = spacer;
            }

            /**
             * Gets the domain.
             *
             * @return the value of domain
             */
            public String getDomain() {
                return domain;
            }

            /**
             * Sets the enabled.
             *
             * @param domain domain
             */
            public void setDomain(final String domain) {
                this.domain = domain;
            }

            /**
             * Gets the prefixes.
             *
             * @return the value of prefixes
             */
            public Set<String> getPrefixes() {
                return prefixes;
            }

            /**
             * Sets the enabled.
             *
             * @param prefixes prefixes
             */
            public void setPrefixes(final Set<String> prefixes) {
                this.prefixes = prefixes;
            }

            /**
             * Gets the prefixes.
             *
             * @return the value of prefixes
             */
            public Set<String> getOrigins() {
                return origins;
            }

            /**
             * Sets the origins.
             *
             * @param origins origins
             */
            public void setOrigins(final Set<String> origins) {
                this.origins = origins;
            }

            /**
             * Gets the originRegex.
             *
             * @return the value of originRegex
             */
            public String getOriginRegex() {
                return originRegex;
            }

            /**
             * Sets the originRegex.
             *
             * @param originRegex originRegex
             */
            public void setOriginRegex(final String originRegex) {
                this.originRegex = originRegex;
            }
        }
    }

    /**
     * The type Scheduler.
     */
    public static class Scheduler {

        private boolean enabled;

        private String type = "fixed";

        private Integer threads = Math.max((Runtime.getRuntime().availableProcessors() << 1) + 1, 16);

        /**
         * Gets enabled.
         *
         * @return the enabled
         */
        public boolean getEnabled() {
            return enabled;
        }

        /**
         * Sets enabled.
         *
         * @param enabled the enabled
         */
        public void setEnabled(final boolean enabled) {
            this.enabled = enabled;
        }

        /**
         * Gets type.
         *
         * @return the type
         */
        public String getType() {
            return type;
        }

        /**
         * Sets type.
         *
         * @param type the type
         */
        public void setType(final String type) {
            this.type = type;
        }

        /**
         * Gets threads.
         *
         * @return the threads
         */
        public Integer getThreads() {
            return threads;
        }

        /**
         * Sets threads.
         *
         * @param threads the threads
         */
        public void setThreads(final Integer threads) {
            this.threads = threads;
        }
    }

    /**
     * gateway trie config.
     */
    public static class GatewayTrieConfig {
        private Long childrenSize = 10000L;

        private Long pathRuleCacheSize = 1000L;

        private Long pathVariableSize = 1000L;

        /**
         * match mode.
         * @see TrieMatchModeEvent
         */
        private String matchMode = TrieMatchModeEvent.ANT_PATH_MATCH.getMatchMode();

        /**
         * get trie children size.
         *
         * @return trie children size
         */
        public Long getChildrenSize() {
            return childrenSize;
        }

        /**
         * set trie children size.
         *
         * @param childrenSize trie children size
         */
        public void setChildrenSize(final Long childrenSize) {
            this.childrenSize = childrenSize;
        }

        /**
         * get path rule cache size.
         *
         * @return path rule cache size
         */
        public Long getPathRuleCacheSize() {
            return pathRuleCacheSize;
        }

        /**
         * set path rule cache size.
         *
         * @param pathRuleCacheSize path rule cache size
         */
        public void setPathRuleCacheSize(final Long pathRuleCacheSize) {
            this.pathRuleCacheSize = pathRuleCacheSize;
        }

        /**
         * get path variable node size.
         *
         * @return path variable node size
         */
        public Long getPathVariableSize() {
            return pathVariableSize;
        }

        /**
         * set path variable node size.
         *
         * @param pathVariableSize path variable node size
         */
        public void setPathVariableSize(final Long pathVariableSize) {
            this.pathVariableSize = pathVariableSize;
        }

        /**
         * get match mode.
         *
         * @return motch mode
         */
        public String getMatchMode() {
            return matchMode;
        }

        /**
         * set match mode.
         *
         * @param matchMode match mode
         */
        public void setMatchMode(final String matchMode) {
            this.matchMode = matchMode;
        }
    }
}
